#include <stdarg.h>
#include "config/type.h"
#include "config/sys_param.h"
#include "lib/stdio.h"
#include "lib/string.h"
#include "user.h"

int gets(char *str, int size);
static void putc(char ch);

static char digits[] = "0123456789ABCDEF";

static void printint(int xx, int base, int sign) {
	char buf[16];
	unsigned int x;
	if (sign && (sign = xx < 0))
		x = -xx;
	else
		x = xx;

	int i = 0;
	do {
		buf[i++] = digits[x % base];
	} while ((x /= base) != 0);

	if (sign)
		buf[i++] = '-';

	while (--i >= 0)
		putc(buf[i]);
}

static void printptr(unsigned long x) {
	int i;
	putc('0');
	putc('x');
	for (i = 0; i < (sizeof(unsigned long) * 2); i++, x <<= 4)
		putc(digits[x >> (sizeof(unsigned long) * 8 - 4)]);
}

// （用户态）格式化输出，目前仅支持 
// 32位: %o (八进制), %d (十进制), %x (十六进制)
// 64位: %p (指针)
// 其他: %s (字符串), %c (字符)
void printf(const char *fmt, ...) {
	va_list ap;
	int i, c;
	char *s;
	
	if (fmt == 0)
		return;
	
	va_start(ap, fmt);
	for (i = 0; (c = fmt[i] & 0xff) != 0; i++) {
		if (c != '%') {
			putc(c);
			continue;
		}
		c = fmt[++i] & 0xff;
		if (c == 0) {
			break;
		}
		switch (c) {
			case 'o':
				printint(va_arg(ap, int), 8, 1);
				break;
			case 'd':
				printint(va_arg(ap, int), 10, 1);
				break;
			case 'x':
				printint(va_arg(ap, int), 16, 1);
				break;
			case 'p':
				printptr(va_arg(ap, unsigned long));
				break;
			case 's':
				if ((s = va_arg(ap, char *)) == 0)
					s = "(null)";
				for (; *s; s++)
					putc(*s);
				break;
			case 'c':
				putc((char)va_arg(ap, int));
				break;
			case '%':
				putc('%');
				break;
			default:
			 	// Print unknown % sequence to draw attention.
			 	putc('%');
			 	putc(c);
			 	break;
		}
	}
}

// 分割字符串，其中 str 为需要分割的字符串, separator 为分隔符，
// 原字符串将被改动，切分符原位置均更改为 '\0'。
// 返回原字符串被分割出的第一个子字符串，如果没有可检索的字符串，则返回一个空指针。
char *strtok(char *str, const char *separator) {
	// str 传了 null 的时候如果上次有记录，则用上一次的下一个位置，不然就为 null
	static char *p_last = null;
	if (str == null && p_last == null)
		return null;
	if (str == null) {
		// 用来返回的当前位置
		str = p_last;
		char *tmp = p_last;
		int len = strlen(separator);
		while (*tmp) {
			for (int i = 0; i < len; ++i) {
				if (*tmp == separator[i]) {
					p_last = tmp + 1;
					*tmp = '\0';
					return str;
				}
			}
			// 走到这里表示当前的 tmp 没有在delimiters中能找到的
			tmp++;
		}
		// 走到这里就是自己走到 \0 的位置，就要将 p_last 设置成为 null
		char *res = p_last;
		p_last = null;
		return res;
	} else {
		char *tmp = str;
		int len = strlen(separator);
		while (*tmp) {
			for (int i = 0; i < len; ++i) {
				// 将原串中的分隔符字符串都设置为'\0'，保证输出res时，只输出第一段，达到分割的效果
				if (*tmp == separator[i]) {
					p_last = tmp + 1;
					*tmp = '\0';
					return str;
				}
			}
			// 分隔符无法对剩下的字符串进行分割
			tmp++;
		}
		// 找不到分割
		char *res = p_last;
		p_last = null;
		return res;
	}
}

// 分割字符串，其中 src 为源字符串的首地址，separator 指定的分割字符，
// dest 为接收分割后子字符串的数组，num 为分割后子字符串的个数。
void split(char *src, const char *separator, char **dest, int *num) {
	char *pNext;
	int count = 0;
	if (!src || strlen(src) == 0)
		return;
	if (!separator || strlen(separator) == 0)
		return;

	pNext = strtok(src, separator);
	while (pNext != null) {
		*dest++ = pNext;
		count++;
		// pr_debug(split, "before pNext = strtok(null, separator)");
		// strtok() 第一个参数传入 null
		// 表示使用函数自身的保留指针定位下一个待处理字符
		pNext = strtok(null, separator);
		// pr_debug(split, "after pNext = strtok(null, separator)");
	}
	*num = count;
}

// 清空字符串中的多余空格
void clean_command(char *src, char *des, unsigned int n) {
	int sp, dp;
	bool start_with_space = true, is_first_space = true;
	for (sp = 0, dp = 0; sp < n; sp++) {
		if (start_with_space) {
			// 去除开头空格
			if (src[sp] != ' ') {
				des[dp++] = src[sp]; 
				start_with_space = false;
			} 
			continue;
		}
		
		if (src[sp] != ' ') {
			des[dp++] = src[sp]; 
			is_first_space = true;
		} else {
			// 去除连续空格
			if (is_first_space) {
				// 是连续空格中的第一个
				des[dp++] = src[sp]; 
				is_first_space = false;
			} else {
				// 不是连续空格中的第一个
				continue;
			}
		}
	}

	// 去除末尾空格
	dp--;
	if (des[dp] == ' ') {
		des[dp] = '\0';
	}
}

#ifdef DEBUG_MODE
#include "debug.h"
#endif

int gets(char *str, int size) {
	read(str, size);
}

static void putc(char ch) {
	write(ch);
}


